package io.hellsinger.navigation.screen

import android.content.Context
import android.content.Intent
import android.os.Bundle
import android.view.View
import io.hellsinger.navigation.route.AndroidRouter

abstract class ScreenController<Args> {
    internal var contentView: View? = null
        private set
    protected var router: AndroidRouter? = null
        private set

    protected var state = INITIALIZED
        private set

    @JvmField
    protected var args: Args? = null

    open val usePopupMode: Boolean = false

    open fun getScreenTitle(): String? = null

    val canSlideBack: Boolean
        get() = !usePopupMode

    internal fun attach(router: AndroidRouter) {
        this.router = router
        state = state or ATTACHED
        onAttachImpl()
    }

    // Attached to navigation controller (there's instance of it)
    protected open fun onAttachImpl() {
    }

    internal fun detach() {
        onDetachImpl()
        router = null
        state = state and ATTACHED.inv()
    }

    // Detached from navigation controller (null-instance)
    protected open fun onDetachImpl() {
    }

    fun setArgs(args: Args) {
        this.args = args
    }

    // System-back button activated.
    internal fun onBackActivated(): Boolean = onBackActivatedImpl()

    internal fun onTransitionFactor(factor: Float) = onTransitionFactorImpl(factor)

    // Swipe-back, animation, etc.
    protected open fun onTransitionFactorImpl(factor: Float) {}

    protected abstract fun onBackActivatedImpl(): Boolean

    // Create view
    internal fun onCreateView(context: Context): View {
        if (contentView == null) contentView = onCreateViewImpl(context)

        return contentView!!
    }

    internal fun onActivityResult(
        requestCode: Int,
        resultCode: Int,
        data: Intent?,
    ): Boolean = onActivityResultImpl(requestCode, resultCode, data)

    /**
     * Called when [NavScreenController] gets result from parent [android.app.Activity]
     * @return true - indicates that result was consumed and there's no need to iterate other
     * controllers in [NavScreenController], false - otherwise
     */
    protected open fun onActivityResultImpl(
        requestCode: Int,
        resultCode: Int,
        data: Intent?,
    ): Boolean = false

    internal fun onRequestPermissionsResult(
        requestCode: Int,
        permissions: Array<out String>,
        grantResults: IntArray,
    ): Boolean = onRequestPermissionsResultImpl(requestCode, permissions, grantResults)

    /**
     * Called when [NavScreenController] gets result from parent [android.app.Activity]
     * @return true - indicates that result was consumed and there's no need to iterate other
     * controllers in [NavScreenController], false - otherwise
     */
    protected open fun onRequestPermissionsResultImpl(
        requestCode: Int,
        permissions: Array<out String>,
        grantResults: IntArray,
    ): Boolean = false

    /**
     * Called when [NavScreenController] going to make screen-transition.
     * Returned content view will be used in animation.
     * Here you can defines your views, listeners to them (like [android.view.View.OnClickListener])
     * Consider that it's not recommended to bind listeners which can start immediately, for that reason
     * use [onFocus] or [onPrepare]
     * @return [android.view.View]
     */
    protected abstract fun onCreateViewImpl(context: Context): View

    internal fun onPrepare() {
        onPrepareImpl()
    }

    /**
     * Called when [NavScreenController] tells that this screen going to show.
     * Here you can bind your data, listeners, etc.
     */
    protected abstract fun onPrepareImpl()

    internal fun onFocus() {
        state = state or FOCUSED
        onFocusImpl()
    }

    // Start Animation?

    /**
     * Called when [NavScreenController] all transition have been applied (like animation)
     * and screen fully visible for user.
     * Here you can bind your data, listeners, etc.
     */
    protected abstract fun onFocusImpl()

    internal fun onHide() {
        state = state and FOCUSED.inv()
        onHideImpl()
    }

    // Unbind listeners
    protected abstract fun onHideImpl()

    internal fun onDestroy() {
        state = state or DESTROYED
        router = null
        onDestroyImpl()
    }

    // Unbind and destroy listeners, clear data, remove and destroy all views
    protected abstract fun onDestroyImpl()

    internal fun onSave(
        buffer: Bundle,
        prefix: String,
    ) = onSaveImpl(buffer, prefix)

    protected abstract fun onSaveImpl(
        buffer: Bundle,
        prefix: String,
    ): Boolean

    internal fun onRestore(
        buffer: Bundle,
        prefix: String,
    ): Boolean = onRestoreImpl(buffer, prefix)

    protected abstract fun onRestoreImpl(
        buffer: Bundle,
        prefix: String,
    ): Boolean

    companion object {
        const val INITIALIZED = 0
        const val ATTACHED = 1 shl 0
        const val FOCUSED = 1 shl 1
        const val DESTROYED = 1 shl 2
    }
}
